package cn.yuemouren.security.app.authentication.openid;

import cn.yuemouren.security.core.properties.SecurityConstants;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Author: Timi
 * @Description: 自定义实现openId过滤器
 * @Date: 2020/7/4 17:38
 * @Version: v1.0
 */
public class OpenIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

        private String openIdParameter = SecurityConstants.SPRING_SECURITY_FORM_OPEN_ID_KEY;
        private String providerIdParameter = SecurityConstants.SPRING_SECURITY_FORM_PROVIDER_ID_KEY;
        private boolean postOnly = true;

        // ~ Constructors
        // ===================================================================================================

	    public OpenIdAuthenticationFilter() {
            super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_OPENID, "POST"));
        }

        // ~ Methods
        // ========================================================================================================

        public Authentication attemptAuthentication(HttpServletRequest request,
                HttpServletResponse response) throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }

            String openId = obtainOpenId(request);
            String providerId = obtainProviderId(request);

            OpenIdAuthenticationToken authRequest = new OpenIdAuthenticationToken(openId,providerId);

            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);

            return this.getAuthenticationManager().authenticate(authRequest);
        }

        /**
         * Enables subclasses to override the composition of the password, such as by
         * including additional values and a separator.
         * <p>
         * This might be used for example if a postcode/zipcode was required in addition to
         * the password. A delimiter such as a pipe (|) should be used to separate the
         * password and extended value(s). The <code>AuthenticationDao</code> will need to
         * generate the expected password in a corresponding manner.
         * </p>
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the password that will be presented in the <code>Authentication</code>
         * request token to the <code>AuthenticationManager</code>
         */
        protected String obtainOpenId(HttpServletRequest request) {
            return request.getParameter(openIdParameter);
        }

        /**
         * Enables subclasses to override the composition of the username, such as by
         * including additional values and a separator.
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the username that will be presented in the <code>Authentication</code>
         * request token to the <code>AuthenticationManager</code>
         */
        protected String obtainProviderId(HttpServletRequest request) {
            return request.getParameter(providerIdParameter);
        }

        /**
         * Provided so that subclasses may configure what is put into the authentication
         * request's details property.
         *
         * @param request that an authentication request is being created for
         * @param authRequest the authentication request object that should have its details
         * set
         */
        protected void setDetails(HttpServletRequest request,
                OpenIdAuthenticationToken authRequest) {
            authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        }

        /**
         * Sets the parameter name which will be used to obtain the username from the login
         * request.
         *
         * @param openIdParameter the parameter name. Defaults to "username".
         */
        public void setOpenIdParameter(String openIdParameter) {
            Assert.hasText(openIdParameter, "openId parameter must not be empty or null");
            this.openIdParameter = openIdParameter;
        }

        /**
         * Sets the parameter name which will be used to obtain the password from the login
         * request..
         *
         * @param providerIdParameter the parameter name. Defaults to "password".
         */
        public void setProviderIdParameter(String providerIdParameter) {
            Assert.hasText(providerIdParameter, "ProviderId parameter must not be empty or null");
            this.providerIdParameter = providerIdParameter;
        }

        /**
         * Defines whether only HTTP POST requests will be allowed by this filter. If set to
         * true, and an authentication request is received which is not a POST request, an
         * exception will be raised immediately and authentication will not be attempted. The
         * <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
         * authentication.
         * <p>
         * Defaults to <tt>true</tt> but may be overridden by subclasses.
         */
        public void setPostOnly(boolean postOnly) {
            this.postOnly = postOnly;
        }

        public final String getOpenIdParameter() {
            return openIdParameter;
        }

        public final String getProviderIdParameter() {
            return providerIdParameter;
        }
}
